package com.conph.moring.core;

import java.lang.reflect.Method;

import com.conph.moring.core.annotation.After;
import com.conph.moring.core.annotation.Aspect;
import com.conph.moring.core.annotation.Before;
import com.conph.moring.core.annotation.ThrowException;
import com.mec.util.PackageScanner;

public class IntercepterScanner {

	/**
	 * 扫描出拦截器们所在的类并逐个对其进行处理，加入到拦截器工厂中
	 * @param packageName 包名称
	 */
	public static void intercepterScanner(String packageName) {
		new PackageScanner() {
			
			@Override
			public void dealClass(Class<?> klass) {
				if (!klass.isAnnotationPresent(Aspect.class)) {
					return;
				}
				//aspect注解的类中集合了各种各样的拦截器。我们将他归类、添加。
				try {
					Object object = klass.newInstance();
					Method[] methods = klass.getDeclaredMethods();
					for (Method method : methods) {
						try {
							if (method.isAnnotationPresent(Before.class)) {
								Before before = method.getAnnotation(Before.class);
								addBeforeInterceptor(klass, object, method, before);
							} else if (method.isAnnotationPresent(After.class)) {
								After after = method.getAnnotation(After.class);
								addAfterInterceptor(klass, object, method, after);
							} else if (method.isAnnotationPresent(ThrowException.class)) {
								ThrowException throwException = method.getAnnotation(ThrowException.class);
								addExceptionInterceptor(klass, object, method, throwException);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}.packageScan(packageName);
	}

	/**
	 * 添加异常拦截器到拦截器工厂中
	 * @param klass 拦截器所在类
	 * @param object 拦截器所在类的实例
	 * @param method 拦截器方法本身
	 * @param throwException 扫描到的注解对象，用于反射构建出IntercepterTargetDefination对象
	 * @throws Exception
	 */
	private static void addExceptionInterceptor(Class<?> klass, Object object, Method method,
			ThrowException throwException) throws Exception {
		//异常拦截器是在相应操作异常时，将具体异常传递给所有该操作的异常拦截器。
		//所以异常拦截器的返回值类型是void，参数类型是Throwable！
		Class<?> returnType = method.getReturnType();
		Class<?>[] paras = method.getParameterTypes();
		
		if(returnType.equals(void.class) || paras.length != 1 || paras[0] != void.class) {
			throw new Exception("拦截器操作\" " + method + "\"不符合规范！");
		}
		Class<?> targetClass = throwException.klass();
		String targetMethodName = throwException.method();
		Method targetMethod = klass.getDeclaredMethod(targetMethodName, Throwable.class);
		IntercepterTargetDefination itd = new IntercepterTargetDefination(targetClass, targetMethod);
		IntercepterMethodDefination imd = new IntercepterMethodDefination(klass, method, object);
		IntercepterFactory intercepterFactory = new IntercepterFactory();
		intercepterFactory.addExceptionIntercepter(itd, imd);
	}

	/**
	 * 添加后置拦截器到拦截器工厂中
	 * @param klass 拦截器所在类
	 * @param object 拦截器所在类的实例
	 * @param method 拦截器方法本身
	 * @param after 扫描到的注解对象，用于反射构建出IntercepterTargetDefination对象
	 * @throws Exception
	 */
	private static void addAfterInterceptor(Class<?> klass,
			Object object, Method method, After after) throws Exception {
		// 将扫描出的后置拦截器进行筛选、添加。
		// 后置拦截器是在相关方法执行完后，他的返回值对象进行加工，并返回加工后的对象。
		// 其中，参数after是传进来注解类型的对象，这个对象中有这这个拦截器方法作用的目标类，类中的目标方法名。
		// 所以后置拦截器的返回值、与参数类型必须是拦截目标方法的返回值类型！
		Class<?> returnType = method.getReturnType();
		Class<?> targetClass = after.klass();
		String targetMethodName = after.method();
		Method targetMethod = targetClass.getDeclaredMethod(targetMethodName, after.parameterTypes());
		
		if(!returnType.equals(targetMethod.getReturnType())) {
			throw new Exception("后置拦截器返回值必须与拦截目标的返回值相同！");
		}
		
		IntercepterTargetDefination itd = new IntercepterTargetDefination(targetClass, targetMethod);
		IntercepterMethodDefination imd = new IntercepterMethodDefination(klass, method, object);
		IntercepterFactory intercepterFactory  = new IntercepterFactory();
		intercepterFactory.addAfterIntercepter(itd, imd);
	}
	/**
	 * 添加前置拦截器到拦截器工厂中
	 * @param klass 拦截器所在类
	 * @param object 拦截器所在类的实例
	 * @param method 拦截器方法本身
	 * @param before 扫描到的注解对象，用于反射构建出IntercepterTargetDefination对象
	 * @throws Exception
	 */
	private static void addBeforeInterceptor(Class<?> klass, Object object,
			Method method, Before before) throws Exception {
		// 将扫描出的前置拦截器进行筛选、添加。
		// 前置拦截器是将拦截的方法的参数进行判断，并返回一个boolean的值来表示这个操作是否允许继续进行。
		// 其中，参数before是扫描到的注解的对象，这个对象中有这这个拦截器方法作用的目标类，目标方法名。
		Class<?> returnType = method.getReturnType();
		if(!returnType.equals(boolean.class)) {
			//返回值不是boolean，抛出异常
			throw new Exception("前置拦截器返回值必须是boolean！\n");
		}
		Class<?> targetClass = before.klass();
		String targetMethodName = before.method();
		Method targetMethod = null;
		targetMethod = targetClass.getDeclaredMethod(targetMethodName, method.getParameterTypes());
		//这里的目标方法是怎么得到的呢，是因为针对前置拦截器，其参数和拦截目标方法的参数是相同的。
		IntercepterTargetDefination itd = new IntercepterTargetDefination(targetClass, targetMethod);
		IntercepterMethodDefination imd = new IntercepterMethodDefination(klass, method, object);
		IntercepterFactory intercepterFactory  = new IntercepterFactory();
		intercepterFactory.addBeforeIntercepter(itd, imd);
	}
}
